﻿#include "FITKGraphObjectPostModelBase.h"

// VTK
#include <vtkProperty.h>
#include <vtkDataSet.h>
#include <vtkMapper.h>
#include <vtkLabelPlacementMapper.h>
#include <vtkFreeTypeLabelRenderStrategy.h>
#include <vtkLabelHierarchy.h>
#include <vtkPointSetToLabelHierarchy.h>
#include <vtkTextProperty.h>
#include <vtkGlyph3D.h>
#include <vtkDistanceToCamera.h>
#include <vtkCamera.h>
#include <vtkRenderer.h>

// Render
#include "FITK_Component/FITKRenderWindowVTK/FITKGraphRender.h"

// Graph and filter
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor2D.h"
#include "FITKGraphCommons.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKDeformFilter.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKPolygonSource.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKMaxMinValueFilter.h"

// Data
#include "FITK_Interface/FITKInterfaceStructuralPost/FITKStaticStruPostVTK.h"
#include "FITK_Interface/FITKInterfaceStructuralPost/FITKCutViewManager.h"

namespace Exchange
{
    FITKGraphObjectPostModelBase::FITKGraphObjectPostModelBase(AbaqusData::FITKDataCase* caseData, Core::FITKAbstractDataObject* postData)
        : FITKGraphObjectPostBase(caseData, postData)
    {
        // Nothing to do here.
    }

    FITKGraphObjectPostModelBase::~FITKGraphObjectPostModelBase()
    {
        // Delete the filters.
        deleteVtkObj(m_maxFilter);
        deleteVtkObj(m_minFilter);
        deleteVtkObjs(m_distToCameraList);

        // Delete the text properties.
        deleteVtkObj(m_textPropertyMax);
        deleteVtkObj(m_textPropertyMin);

        // Delete the source.
        deleteVtkObj(m_polygonSource);
    }

    void FITKGraphObjectPostModelBase::initializeLabelsActors()
    {
        // Create filters.
        m_maxFilter = FITKMaxMinValueFilter::New();
        m_maxFilter->SetPeakDataType(PeakType::PeakMax);
        
        m_minFilter = FITKMaxMinValueFilter::New();
        m_minFilter->SetPeakDataType(PeakType::PeakMin);

        // Create text properties.
        //@{
        m_textPropertyMax = vtkTextProperty::New();
        m_textPropertyMax->SetBold(false);
        m_textPropertyMax->SetFontSize(16.);

        double colorMax[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(Qt::yellow, colorMax);
        m_textPropertyMax->SetColor(colorMax);
        m_textPropertyMax->SetFontFamilyToArial();

        m_textPropertyMin = vtkTextProperty::New();
        m_textPropertyMin->SetBold(false);
        m_textPropertyMin->SetFontSize(16);

        double colorMin[3] = { 0 ,0, 0 };
        FITKGraphCommons::QColorToDouble3(Qt::yellow, colorMin);
        m_textPropertyMin->SetColor(colorMin);
        m_textPropertyMin->SetFontFamilyToArial();
        //@}

        // Create the max-min value text actor and mapper.
        m_fActor2DMax = createTextActor(m_maxFilter->GetOutputPort(), m_textPropertyMax, m_maxFilter->GetLabelArrayName());
        m_fActor2DMin = createTextActor(m_minFilter->GetOutputPort(), m_textPropertyMin, m_minFilter->GetLabelArrayName());

        // Create the data source.
        m_polygonSource = FITKPolygonSource::New();
        m_polygonSource->SetGeneratePolygon(false);
        m_polygonSource->SetGeneratePolyline(true);
        m_polygonSource->SetRadius(1);
        m_polygonSource->SetNumberOfSides(10);

        // Create the max-min value glyph actor.
        m_fActorMax = createGlyphActor(m_maxFilter->GetOutputPort(), Qt::yellow);
        m_fActorMin = createGlyphActor(m_minFilter->GetOutputPort(), Qt::yellow);

        // Create the label line actor.
        //@{
        m_fActorLineMax = FITKGraphActor::New();
        m_fActorLineMax->setGraphObject(this);
        m_fActorLineMax->SetPickable(false);
        m_fActorLineMax->setScalarVisibility(false);
        m_fActorLineMax->setInputConnection(m_maxFilter->GetOutputPort());

        m_fActorLineMin = FITKGraphActor::New();
        m_fActorLineMin->setGraphObject(this);
        m_fActorLineMin->SetPickable(false);
        m_fActorLineMin->setScalarVisibility(false);
        m_fActorLineMin->setInputConnection(m_minFilter->GetOutputPort());

        addActor(m_fActorLineMax);
        addActor(m_fActorLineMin);
        //@}
    }

    void FITKGraphObjectPostModelBase::updateMaxMinActorVisible(bool visibility)
    {
        // Text actors.
        //@{
        if (m_fActor2DMax)
        {
            m_fActor2DMax->SetVisibility(visibility);
            m_fActor2DMax->update();
        }

        if (m_fActor2DMin)
        {
            m_fActor2DMin->SetVisibility(visibility);
            m_fActor2DMin->update();
        }
        //@}

        // Glyph actors.
        //@{
        if (m_fActorMax)
        {
            m_fActorMax->SetVisibility(visibility);
            m_fActorMax->update();
        }

        if (m_fActorMin)
        {
            m_fActorMin->SetVisibility(visibility);
            m_fActorMin->update();
        }
        //@}

        // Line actors.
        //@{
        if (m_fActorLineMax)
        {
            m_fActorLineMax->SetVisibility(visibility);
            m_fActorLineMax->update();
        }

        if (m_fActorLineMin)
        {
            m_fActorLineMin->SetVisibility(visibility);
            m_fActorLineMin->update();
        }
        //@}
    }

    FITKGraphActor2D* FITKGraphObjectPostModelBase::createTextActor(vtkAlgorithmOutput* output, vtkTextProperty* textProperty, QString arrayName)
    {
        if (!output || !textProperty)
        {
            return nullptr;
        }

        // Create actor 2D.
        FITKGraphActor2D* fActor2D = FITKGraphActor2D::New();
        fActor2D->SetPickable(false);
        fActor2D->setGraphObject(this);
        fActor2D->setActorType(ActorType2D::TextActor);

        // Text mapper and strategy.
        //@{
        vtkSmartPointer<vtkPointSetToLabelHierarchy> textData = vtkSmartPointer<vtkPointSetToLabelHierarchy>::New();
        textData->SetInputConnection(output);
        textData->SetTargetLabelCount(1);
        textData->SetMaximumDepth(15);
        textData->SetLabelArrayName(QStringToCharA(arrayName));
        textData->SetTextProperty(textProperty);
        textData->Update();

        vtkSmartPointer<vtkLabelPlacementMapper> mapperText = vtkSmartPointer<vtkLabelPlacementMapper>::New();
        mapperText->UseDepthBufferOff();
        mapperText->SetShapeToNone();
        mapperText->SetStyleToOutline();
        mapperText->UseUnicodeStringsOff();

        vtkSmartPointer<vtkFreeTypeLabelRenderStrategy> strategyText = vtkSmartPointer<vtkFreeTypeLabelRenderStrategy>::New();

        // Set input connecton of label hierarchy and strategy.
        mapperText->SetInputConnection(textData->GetOutputPort());
        mapperText->SetRenderStrategy(strategyText);

        // Show all labels.
        mapperText->SetPlaceAllLabels(true);
        mapperText->SetMaximumLabelFraction(1.);
        mapperText->SetIteratorType(vtkLabelHierarchy::FULL_SORT);
        fActor2D->SetMapper(mapperText);
        //@}

        addActor(fActor2D);

        return fActor2D;
    }

    FITKGraphActor* FITKGraphObjectPostModelBase::createGlyphActor(vtkAlgorithmOutput* output, QColor color)
    {
        if (!output)
        {
            return nullptr;
        }

        double color3[3]{ 0., 0., 0. };
        FITKGraphCommons::QColorToDouble3(color, color3);

        // Create actor.
        FITKGraphActor* fActor = FITKGraphActor::New();
        fActor->SetPickable(false);
        fActor->setGraphObject(this);
        fActor->setScalarVisibility(false);
        fActor->GetProperty()->SetColor(color3);
        fActor->GetProperty()->SetLineWidth(2);

        // For fixed size.
        //@{
        vtkSmartPointer<vtkGlyph3D> glyph = vtkSmartPointer<vtkGlyph3D>::New();
        vtkDistanceToCamera* distanceToCamera = vtkDistanceToCamera::New();
        m_distToCameraList.push_back(distanceToCamera);
        distanceToCamera->SetScreenSize(5);

        // Set the input.
        distanceToCamera->SetInputConnection(output);

        // Add the point array name need to be used.
#if VTK_MAJOR_VERSION < 8
        glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, "DistanceToCamera");
#else
        glyph->SetInputArrayToProcess(0, 0, 0, vtkDataObject::FIELD_ASSOCIATION_POINTS, distanceToCamera->GetDistanceArrayName());
#endif

        glyph->SetInputConnection(distanceToCamera->GetOutputPort());
        glyph->SetScaling(true);
        glyph->SetScaleModeToScaleByScalar();
        glyph->SetVectorModeToVectorRotationOff();

        // Set the soure data and input data.
        glyph->SetSourceConnection(m_polygonSource->GetOutputPort());

        fActor->setInputConnection(glyph->GetOutputPort());
        addActor(fActor);

        return fActor;
    }

    void FITKGraphObjectPostModelBase::changeField(int& type, QString& name, QString& compName)
    {
        // Change the color array first.
        this->Superclass::changeField(type, name, compName);

        // Change the max and min filter's parameters.
        if (type != 0)
        {
            int nComp = 0;
            int index = getComponentIndex(m_fActor, type, name, compName, nComp);

            if (m_maxFilter)
            {
                m_maxFilter->SetArrayName(QStringToCharA(name));
                m_maxFilter->SetComponentIndex(index);
                m_maxFilter->SetArrayType(type);
            }

            if (m_minFilter)
            {
                m_minFilter->SetArrayName(QStringToCharA(name));
                m_minFilter->SetComponentIndex(index);
                m_minFilter->SetArrayType(type);
            }
        }

        // Update the actor 2D.
        updateMaxMinActorVisible(type != 0 && m_showMaxAndMin);
    }

    void FITKGraphObjectPostModelBase::changeField(int& type, QString& name, int& compIndex, QString& compName)
    {
        // Change the color array first.
        this->Superclass::changeField(type, name, compIndex, compName);

        // Change the max and min filter's parameters.
        if (type != 0)
        {
            if (m_maxFilter)
            {
                int nComp = 0;
                int index = getComponentIndex(m_fActor, type, name, compName, nComp);

                m_maxFilter->SetArrayName(QStringToCharA(name));
                m_maxFilter->SetComponentIndex(index);
                m_maxFilter->SetArrayType(type);
            }
        }

        // Update the actor 2D.
        updateMaxMinActorVisible(type != 0 && m_showMaxAndMin);
    }

    void FITKGraphObjectPostModelBase::setMaxAndMinVisible(bool visibile)
    {
        m_showMaxAndMin = visibile;

        // Update the actor 2D.
        updateMaxMinActorVisible(m_showContour && m_showMaxAndMin && getDataVisibility());
    }

    void FITKGraphObjectPostModelBase::setFITKRender(Comp::FITKGraphRender* render)
    {
        if (!render)
        {
            return;
        }

        vtkRenderer* renderer = render->getRenderer();
        if (!renderer)
        {
            return;
        }

        // Set the renderer for calculating size.
        for (vtkDistanceToCamera* obj : m_distToCameraList)
        {
            if (obj)
            {
                obj->SetRenderer(renderer);
            }
        }

        // Initialize the normal.
        updateCameraNormal();

        // Save the render.
        this->Superclass::setFITKRender(render);
    }

    void FITKGraphObjectPostModelBase::updateCameraNormal()
    {
#if QT_NO_DEBUG
        if (!m_fRender)
        {
            return;
        }

        vtkRenderer* renderer = m_fRender->getRenderer();
        if (!renderer || !m_polygonSource)
        {
            return;
        }

        vtkCamera* camera = renderer->GetActiveCamera();
        if (camera)
        {
            m_polygonSource->SetNormal(camera->GetViewPlaneNormal());
        }
#endif
    }

    void FITKGraphObjectPostModelBase::setVisible(bool visibility)
    {
        // Show or hide the model actor.
        if (m_fActor)
        {
            m_fActor->SetVisibility(visibility);
        }

#ifdef RENDER_VOLUME_TEST
        if (m_volume)
        {
            m_volume->SetVisibility(visibility);
        }
#endif

        // Show or hide scalar bar.
        updateScalarBarVisible();

        // Show or hide the label actors.
        updateMaxMinActorVisible(visibility && m_showMaxAndMin);
    }

    void FITKGraphObjectPostModelBase::setPickable(bool isPickable, int dim)
    {
        Q_UNUSED(dim);

        // Set pickable for the model actor only.
        if (m_fActor)
        {
            m_fActor->SetPickable(isPickable);
        }
    }
}   // namespace Exchange